<?php
declare (strict_types=1);

namespace app\exam\logic\admin;

use app\admin\logic\system\Log;
use app\common\exception\LogicException;
use app\common\logic\category\Category;
use app\exam\model\admin\ModuleSpecial as ModuleSpecialModel;
use app\exam\validate\admin\ModuleSpecial as ModuleSpecialValidate;
use think\facade\Db;
use think\Model;

/**
 * Class ModuleSpecial
 *
 * @package app\exam\logic\admin
 */
class ModuleSpecial extends ModuleSpecialModel
{
    /**
     * 获取树形结构列表
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/29
     *
     * @param int $moduleId
     * @param int $pid
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getTreeList(int $moduleId, int $pid = 0)
    {
        $datas = $this->where('module_id', $moduleId)->where('pid', $pid)->select();

        if (!$datas->isEmpty()) {
            foreach ($datas as $data) {
                if ($this->hasChildren($data->getAttr('id'))) {
                    $data->children = $this->getTreeList($moduleId, $data->getAttr('id'));
                }
            }
        }

        return $datas;
    }

    /**
     * 是否含有子项
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/31
     *
     * @param int $specialId
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function hasChildren(int $pid): bool
    {
        return !!$this->where('pid', $pid)->count();
    }

    /**
     * 解析专项每级包含的试题数量
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-02-04
     *
     * @param  [type] $tree [description]
     * @return [type]       [description]
     */
    public function parseTreeTopicCount($tree)
    {
        foreach ($tree as $v) {
            if (isset($v->children)) {
                $count = $this->computeCount($v->children);
                if ($v->topic_count != $count) {
                    $v->topic_count = $count;
                    $v->save();
                }
                $this->parseTreeTopicCount($v->children);
            }
        }
        return $tree;
    }

    /**
     * 计算当级包含的试题数量
     * Author 亓官雨树<lucky.max@foxmail.com>
     * Date 2021-02-04
     *
     * @param  [type]  $tree  [description]
     * @param integer $count [description]
     * @return [type]         [description]
     */
    public function computeCount($tree, $count = 0)
    {
        foreach ($tree as $v) {
            if (isset($v->children)) {
                $count = $this->computeCount($v->children, $count);
            } else {
                $count += $v->topic_count;
            }
        }
        return $count;
    }

    /**
     * 根据ID获取专项信息
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/26
     *
     * @param int $id
     * @return Model
     */
    public function getSpecialById(int $id): Model
    {
        return $this->findOrFail($id);
    }

    /**
     * 新建专项
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/31
     *
     * @param int   $moduleId
     * @param array $data
     */
    public function createSpecial(int $moduleId, array $data)
    {
        $data['module_id'] = $moduleId;
        validate(ModuleSpecialValidate::class)->scene('create')->check($data);

        $level = 1;
        $pid   = $data['pid'] ?? 0;
        if ($pid) {
            $level = (int)$this->where('id', $data['pid'])->value('level') + 1;
        }

        $save = [
            'module_id'             => $moduleId,
            'title'                 => $data['title'],
            'price'                 => $level == 1 ? $data['price'] : 0,
            'level'                 => $level,
            'pid'                   => $pid,
            'credit_redeem'         => $data['credit_redeem'] ?? 0,
            'credit_redeem_percent' => $data['credit_redeem_percent'] ?? 0,
        ];

        if (isset($data['category'])) {
            $save['category'] = $save['fullcategorypath'] = $data['category'];
        } else if ($pid) {
            $fullCate = $this->getParentFullCate($pid);
            if ($fullCate) {
                $save['category'] = $save['fullcategorypath'] = $fullCate;
            }
        }
        $re = $this::create($save);
        if ($re->isEmpty()) {
            throw new LogicException('新建失败，请重试');
        }

        Log::write('新建专项【' . $data['title'] . '/' . $re->id . '】', 'special', 'create', true);
    }

    /**
     * 编辑专项
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/31
     *
     * @param int   $specialId
     * @param int   $moduleId
     * @param array $data
     */
    public function editSpecial(int $specialId, int $moduleId, array $data)
    {
        $special           = $this->getSpecialById($specialId);
        $data['id']        = $specialId;
        $data['module_id'] = $moduleId;
        validate(ModuleSpecialValidate::class)->scene('edit')->check($data);

        $special->title                 = $data['title'];
        $special->credit_redeem         = $data['credit_redeem'] ?? 0;
        $special->credit_redeem_percent = $data['credit_redeem_percent'] ?? 0;

        if ($special->getAttr('pid') == 0) {
            $special->price = $data['price'] ?? 0;
            if (isset($data['category'])) {
                $category = $data['category'];
                if (is_array($category)) {
                    $special->category         = $category;
                    $special->fullcategorypath = $category;
                } else {
                    $special->category         = json_decode($category, true);
                    $special->fullcategorypath = app(Category::class)->cateStrToArr($special->category);
                }
                // 修改子知识点分类
                $this->wherePid($specialId)->select()->each(function ($i) use ($category) {
                    $i->category         = $category;
                    $i->fullcategorypath = $category;
                    $i->save();
                });
            }
        }

        if (!$special->save()) {
            throw new LogicException('修改专项失败');
        }

        Log::write('编辑专项【' . $data['title'] . '/' . $specialId . '】', 'special', 'update', true);
    }

    /**
     * 编辑某个字段
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/31
     *
     * @param int    $specialId
     * @param string $field
     * @param        $value
     * @return Model
     */
    public function editSpecialField(int $specialId, string $field, $value)
    {
        // 信息
        $special = $this->getSpecialById($specialId);

        $special->$field = $value;

        if (!$special->save()) {
            throw new LogicException("更新失败");
        }

        Log::write('编辑专项【' . $special->title . '/' . $specialId . '】', 'special', 'update', true);

        // 返回值
        return $special;
    }

    /**
     * 删除专项
     * Author: Martinsun <syh@sunyonghong.com>
     * Date: 2021/1/31
     *
     * @param int $specialId
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function deleteSpecial(int $specialId)
    {
        $special = $this->getSpecialById($specialId);

        if ($this->hasChildren($specialId)) {
            throw new LogicException('含有子专项，不允许删除');
        }

        Db::startTrans();
        try {
            // 顶级
            $pids  = $this->getAllPidsBySpecial($special);
            $count = $special->topic_count;
            $this->whereIn('id', $pids)->select()->each(function ($i) use ($count) {
                $i->topic_count -= $count;
                if (!$i->save()) throw new LogicException("删除失败");
            });
            if (!$special->delete()) throw new LogicException('删除失败');
            DB::commit();
        } catch (LogicException $e) {
            Db::rollback();
            throw new LogicException($e->getMssage());
        }

        Log::write('删除专项【' . $special['title'] . '/' . $specialId . '】', 'special', 'delete', true);
    }

    private function getTopDataFullCate($pid)
    {
        $data = $this->findOrEmpty($pid);
        if ($data->isEmpty()) return null;
        if ($data->pid) return $this->getTopDataFullCate($data->pid);
        return $data->fullcategorypath;
    }

    private function getParentFullCate($pid)
    {
        $data = $this->findOrEmpty($pid);
        return $data->isEmpty() ? null : $data->fullcategorypath;
    }

    private function getAllPidsBySpecial(Model $special, $pids = [])
    {
        if ($special->pid) {
            array_push($pids, $special->pid);
            $pdata = $this->find($special->pid);
            return $this->getAllPidsBySpecial($pdata, $pids);
        }
        return $pids;
    }
}